import os
from typing import Literal
from pathlib import PurePath

import httpx
from fastapi import Depends, BackgroundTasks
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker

from my_webdav import PROJ_PATH
from my_webdav.config import app_setting
from .cache_model import CacheLayer, Base
# from my_webdav.resource_model.client_115.client import ClientFor115
from ..client_115.client import ClientFor115
from .data_operate import OperateLayer
from .res_model_115 import info_to_model

db_path = os.path.join(PROJ_PATH, 'data', "cache_data.db")
if app_setting.PAN_115_CACHE_IN_MEM:
    uri = "sqlite+aiosqlite:///:memory:"
else:
    uri = f"sqlite+aiosqlite:///{db_path}"
engine = create_async_engine(uri)
DBSession = sessionmaker(engine, AsyncSession, expire_on_commit=False)

client_param = {
    "proxies": app_setting.PROXY_ADDR,
    "verify": app_setting.VERIFY_CA
}

async_client = httpx.AsyncClient(**client_param)
sync_client = httpx.Client(**client_param)


async def prepare_db():
    if app_setting.PAN_115_MOUNT_PATH:
        async with engine.begin() as conn:
            await conn.run_sync(Base.metadata.create_all)


async def close_client():
    sync_client.close()
    await async_client.aclose()


async def get_db_session():
    async with DBSession.begin() as session:
        yield session


async def get_httpx_async_client():
    yield async_client


async def get_httpx_sync_client():
    yield sync_client


async def get_operate_layer(
        bck_task: BackgroundTasks, db_session=Depends(get_db_session),
        a_client=Depends(get_httpx_async_client), s_client=Depends(get_httpx_sync_client)
):
    client_115 = ClientFor115(
        root_id=app_setting.PAN_115_ROOT_CID,
        cookies=app_setting.PAN_115_COOKIE,
        async_client=a_client,
        sync_client=s_client
    )
    cache_layer = CacheLayer(db_session)

    op_layer = OperateLayer(client_115, cache_layer, bck_task)
    yield op_layer


async def refresh_115_cache():
    if app_setting.PAN_115_MOUNT_PATH:
        root_id = app_setting.PAN_115_ROOT_CID
        cookie = app_setting.PAN_115_COOKIE
        async with DBSession.begin() as session:
            async with httpx.AsyncClient(**client_param) as a_client:
                with httpx.Client(**client_param) as s_client:
                    c_115 = ClientFor115(root_id, cookie, a_client, s_client, 5.0)
                    cache = CacheLayer(session)
                    op_layer = OperateLayer(c_115, cache)
                    await op_layer.refresh_cache(root_id, app_setting.PAN_115_MOUNT_PATH)


async def pan115_model_adapter(
        path: PurePath, depth: Literal[0, 1],
        op_layer: OperateLayer = Depends(get_operate_layer)
):
    """
    根据path返回对应的资源模型
    :param depth:
    :param path:
    :param op_layer:
    :return:
    """
    members = await op_layer.search_by_path(path, depth)
    model = info_to_model(members[0], op_layer, path)
    if len(members) > 1:
        children = [info_to_model(m, op_layer, dir_path=path) for m in members[1:]]
        model.add_children(children)
    return model
